expo(react native)のreact-nativationのbottom tabで任意のsvgアイコンを表示する方法を解説します。 svgは.svgファイルから読み込んで表示させます。こうしておくと、figmaなどからDLしたものをそのまま読み込ませれるから便利です。

また、assetsは@assetsでインポートできるようエイリアスを設定します。

1. ライブラリのインストール

まずはreact-navigationのbottom-tabsとsvg関係のライブラリをインストールします。

関連記事

https://reactnavigation.org/docs/bottom-tab-navigator https://docs.expo.dev/versions/latest/sdk/svg/ https://blog.logrocket.com/how-to-use-svgs-react-native-tutorial-with-examples/

インストールコマンド

$ yarn add @react-navigation/bottom-tabs
$ npx expo install react-native-svg
$ yarn add -D react-native-svg-transformer
$ yarn add -D babel-plugin-inline-import

ビルド時にsvgファイルを読み込んだり、importできるようにするため、transformerなどもここで一緒にインストールします。

2. 設定

metro.config.js

metro.config.jsを編集します、ない人はプロジェクト直下に作成して下さい。

const { getDefaultConfig } = require('metro-config')

module.exports = (async () => {
  const {
    resolver: { sourceExts, assetExts },
  } = await getDefaultConfig()
  return {
    transformer: {
      babelTransformerPath: require.resolve('react-native-svg-transformer'),
    },
    resolver: {
      assetExts: assetExts.filter((ext) => ext !== 'svg'),
      sourceExts: [...sourceExts, 'svg'],
    },
  }
})()

babel.config.js

エイリアスの設定や、svgファイル読み込みの設定を行います。

module.exports = function (api) {
  api.cache(true)
  return {
    presets: ['babel-preset-expo'],
    plugins: [
      [
        'module-resolver',
        {
          alias: {
            '@': './src',
            '@assets': './assets',
          },
          extensions: ['.js', '.jsx', '.ts', '.tsx', '.svg'],
        },
      ],
      [
        'babel-plugin-inline-import',
        {
          extensions: ['.svg'],
        },
      ],
    ],
  }
}

3. アイコンの定義

私の場合は、アイコン表示用のatom(コンポーネント)を追加して、アイコンの定義、表示を一元管理するようにしました。

IconAtomType.ts

export const IconType = {
  Home: 'home',
  Favorite: 'favorite',
} as const
export type IconType = typeof IconType[keyof typeof IconType]

なんかenumの書き方は最近嫌われているみたいで、あんまり好きじゃないのですけどこっちの定義の仕方にしました。 https://weseek.co.jp/tech/1609/

Icon.tsx

requireで@assetsでsvgの読み込みをします。

import React from 'react'
import { IconType } from '@/types/atoms/IconAtomType'

const Home = require('@assets/svg/home.svg').default
const Favorite = require('@assets/svg/favorite.svg').default

interface IconProps {
  type: IconType
}

/**
 * アイコン表示
 */
const Icon: React.FC<IconProps> = ({ type }) => {
  switch (type) {
    case IconType.Home:
      return <Home />
    case IconType.Favorite:
      return <Favorite />
  }
}

export default Icon

4. bottom tabの定義

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import FavoritePage from '@/pages/FavoritePage'
import HomePage from '@/pages/HomePage'
import Icon from '@/components/atoms/Icon'
import { IconType } from '@/types/atoms/IconAtomType'

const Tab = createBottomTabNavigator()

const RouterStack = () => {
  return (
    <Tab.Navigator
      initialRouteName={'home'}
      screenOptions={({ route }) => ({
        tabBarIcon: ({ focused, color, size }) => {
          switch (route.name) {
            case 'home':
              return <Icon type={IconType.Home} />
            case 'favorite':
              return <Icon type={IconType.Favorite} />
          }
          return undefined
        },
      })}
    >
      <Tab.Screen name={'home'} component={HomePage} />
      <Tab.Screen name={'favorite'} component={FavoritePage} />
    </Tab.Navigator>
  )
}

export default RouterStack

ボトムタブに任意のsvgアイコンを表示できました。 expo-svg